﻿using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Data.OleDb;
using LocalView;
using DataType;
using Google.GData.Client;
using Google.Contacts;
using Google.Documents;
using Google.GData.Documents;
using System.IO;
using Google.GData.AccessControl;
using Word = Microsoft.Office.Interop.Word;
using Utilities;

namespace OfficeInCloudWord2007
{
    public class WordLocalView : ILocalView
    {
        public delegate void StateNotify(string key);
        public enum Permission { reader, writer };

        private global::System.Object missing = global::System.Type.Missing;
        public DbManager m_db;

        public Word.Application m_App;
        public DocumentInfo m_DocInfo;

        public event EventHandler<Credentials> Login;
        public event EventHandler Logout;
        public event EventHandler<FeedWrapper<Contact>> GetContacts;
        public event EventHandler<FeedWrapper<Document>> GetDocuments;
        public event EventHandler<FeedWrapper<Document>> GetFolders;
        public event EventHandler<DocumentInfo> UploadDocument;
        public event EventHandler<DocumentInfo> UpdateDocument;
        public event EventHandler<DownloadInfo> DownloadDocument;
        public event EventHandler<ShareInfoCollection> ShareDocument;
        public event EventHandler<ShareInfoCollection> GetDocumentAcl;
        public event EventHandler<DocumentInfo> LookForDocument;

        public event EventHandler<DocumentInfo> OpenDocumentInCloud;

        private Word.ApplicationEvents_Event m_excelEvents;
        public StateNotify m_LoadState;
        public StateNotify m_StoreState;

        public WordLocalView(Word.Application App, DbManager db)
        {
            m_App = App;
            m_db = db;
            m_DocInfo = null;

            //m_excelEvents = (Excel.AppEvents_Event)m_App;
            //m_excelEvents.WorkbookActivate +=
            //    new Excel.AppEvents_WorkbookActivateEventHandler(excelEvents_WorkbookActivate);
            //m_excelEvents.WorkbookDeactivate +=
            //    new Excel.AppEvents_WorkbookDeactivateEventHandler(excelEvents_WorkbookDeActivate);
        }

        void excelEvents_WorkbookDeActivate(Word.Document Wb)
        {
            m_StoreState(Wb.Path);
        }

        void excelEvents_WorkbookActivate(Word.Document Wb)
        {
            m_LoadState(Wb.Path);
        }

        public void DoLogin(Credentials cred)
        {
            if (Login != null)
                Login(this, cred);
        }

        public void DoLogOut()
        {
            if (Logout != null)
                Logout(this, new EventArgs());
        }

        public void DoDownloadDocument(DownloadInfo info)
        {
            if (DownloadDocument != null)
                DownloadDocument(this, info);
        }

        public void DoGetContacts(FeedWrapper<Contact> feed)
        {
            if (GetContacts != null)
                GetContacts(this, feed);
        }

        public void DoGetContactsAsync(NotifyDelegate<Contact> Notify)
        {
            if (GetContacts != null)
            {
                DoIterationASync<Contact> Iteration = new DoIterationASync<Contact>(this.GetContacts, Notify, this.dummy);
                Iteration.StartAction();
            }
        }

        public void DoGetDocuments(FeedWrapper<Document> feed)
        {
            if (GetDocuments != null)
                GetDocuments(this, feed);
        }

        public void DoGetDocumentsAsync(NotifyDelegate<Document> Notify)
        {
            if (GetDocuments != null)
            {
                DoIterationASync<Document> Iteration = new DoIterationASync<Document>(this.GetDocuments, Notify, this.dummy);
                Iteration.StartAction();
            }
        }

        private void ShareActiveDocument(string fullName, Word.WdSaveFormat format)
        {

            /*
          
            m_App.ActiveDocument.SaveAs(
                                        fullName,
                                        fileFormat,
                                        ref missing,
                                        ref missing,
                                        ref missing,
                                        ref missing,
                                        ref missing,
                                        ref missing,
                                        ref missing, 



            m_App.ActiveWorkbook.SaveAs(fullName,
                                        format,
                                        missing, //password
                                        missing, //writeRefPassword
                                        missing, //readOnlyRecommended
                                        missing, //createBackup
                                        Excel.XlSaveAsAccessMode.xlShared,
                                        Excel.XlSaveConflictResolution.xlUserResolution,
                                        missing,
                                        missing,
                                        missing,
                                        missing);
            
            throw new NotImplementedException(); 
            */

        }

        public void DoSaveDocumentInCloud()
        {

            ShareActiveDocument(m_App.ActiveDocument.FullName, (Word.WdSaveFormat)m_App.ActiveDocument.SaveFormat);
            if (UploadDocument != null)
            {
                m_DocInfo = new DocumentInfo(m_App.ActiveDocument.FullName, m_App.ActiveDocument.Name);
                UploadDocument(this, m_DocInfo);
                DoRegisterLocalCopy(m_DocInfo);
            }
            
             
        }

        public void DoLookupRemoteCopy(DocumentInfo info)
        {
            try
            {
                using (DbManager.DBConnection con = m_db.CreateConnection())
                {
                    string command = "SELECT ID, GoogleDocID, LocalFileName FROM FileLinks WHERE LocalFileName = @ID";

                    OleDbParameter[] param = new OleDbParameter[1];
                    param[0] = new OleDbParameter("@ID", info.m_FullName);

                    OleDbDataReader reader = con.ExecuteQuery(command, param);
                    reader.Read();
                    info.m_RemoteId = (string)reader["GoogleDocID"];
                    if (LookForDocument != null)
                        LookForDocument(this, info);
                }
            }
            catch (Exception e)
            {
                System.Console.Write(e.Message);
            }
        }

        public void DoLookupLocalCopy(DocumentInfo info)
        {
            try
            {
                using (DbManager.DBConnection con = m_db.CreateConnection())
                {
                    string command = "SELECT ID, GoogleDocID, LocalFileName FROM FileLinks WHERE GoogleDocID = @ID";

                    OleDbParameter[] param = new OleDbParameter[1];
                    param[0] = new OleDbParameter("@ID", info.m_DocEntry.Id.AbsoluteUri);

                    OleDbDataReader reader = con.ExecuteQuery(command, param);
                    reader.Read();
                    info.m_FullName = (string)reader["LocalFileName"];
                }
            }
            catch (Exception e)
            {
                System.Console.Write(e.Message);
            }
        }

        public void DoRegisterLocalCopy(DocumentInfo info)
        {
            try
            {
                using (DbManager.DBConnection con = m_db.CreateConnection())
                {
                    // it can't exist two rows with the same path to the local path
                    string commandDel = "DELETE FROM FileLinks WHERE LocalFileName = @LOCAL";
                    OleDbParameter[] paramDel = new OleDbParameter[1];
                    paramDel[0] = new OleDbParameter("@LOCAL", info.m_FullName);
                    con.ExecuteNonQuery(commandDel, paramDel);

                    string command = "INSERT INTO FileLinks (GoogleDocID, LocalFileName) VALUES (@ID, @LOCAL)";

                    OleDbParameter[] param = new OleDbParameter[2];
                    param[0] = new OleDbParameter("@ID", info.m_DocEntry.Id.AbsoluteUri);
                    param[1] = new OleDbParameter("@LOCAL", info.m_FullName);

                    con.ExecuteNonQuery(command, param);
                }
            }
            catch (Exception e)
            {
                System.Console.Write(e.Message);
            }
        }

        private void MergeDocumentWith(string serverCopyPath)
        {
            /*
            Excel.Workbook wb = m_App.ActiveWorkbook;
            string suffix = "_copy.xls";
            string copy_path = wb.FullName;
            
            // never save copy before, so save it at first time
            if (!copy_path.EndsWith(suffix, System.StringComparison.CurrentCultureIgnoreCase))
            {
                copy_path = copy_path.Replace(".xls", "_copy.xls");
                wb.SaveCopyAs(copy_path);
            }

            Excel.Workbook localCopy = m_App.Workbooks.Open(copy_path,
                                   missing, missing, missing, missing, missing, missing, missing,
                                   missing, missing, missing, missing, missing, missing, missing);
            m_App.ActiveWindow.Visible = false;

            Excel.Workbook serverCopy = m_App.Workbooks.Open(serverCopyPath,
                                   missing, missing, missing, missing, missing, missing, missing,
                                   missing, missing, missing, missing, missing, missing, missing);
            m_App.ActiveWindow.Visible = false;

            Int32 lcSheetsCount = localCopy.Sheets.Count;
            Int32 rsSheetsCount = serverCopy.Sheets.Count;
            for (Int32 sh = 1; sh <= rsSheetsCount; ++sh)
            {
                Excel.Worksheet serverWorksheet = serverCopy.Sheets[sh] as Excel.Worksheet;
                Excel.Worksheet localWorksheet = null;
                if (sh <= lcSheetsCount)
                    localWorksheet = localCopy.Sheets[sh] as Excel.Worksheet;
                else
                    localWorksheet = localCopy.Sheets.Add(missing, localCopy.Sheets[sh - 1], missing, missing) as Excel.Worksheet;

                //it take hours to iterate through all cells
                Int32 rowCount = localWorksheet.Cells.Rows.Count;
                Int32 columnCount = localWorksheet.Cells.Columns.Count;

                //current limitation in order to avoid performance issue
                for (Int32 i = 1; i <= 20; ++i)
                    for (Int32 j = 1; j <= 40; ++j)
                        if ((((Excel.Range)serverWorksheet.Cells[i, j]).Value2 != null) &&
                            (((Excel.Range)serverWorksheet.Cells[i, j]).Value2.ToString().Length != 0))
                            localWorksheet.Cells[i, j] = serverWorksheet.Cells[i, j];
            }

            localCopy.Close(true, missing, false);
            serverCopy.Close(true, missing, false);

            //merge shared copy with original
            wb.Save();
            wb.MergeWorkbook(copy_path);
            wb.Save();
            */
            throw new NotImplementedException();
        }

        public void DoUpdateDocument()
        {
            /*
            try{
                if (UpdateDocument != null)
                {
                    m_App.ActiveWorkbook.Save();
                    UpdateDocument(this, m_DocInfo);
                }
            }
             catch(GDataRequestException e)
            {
                //create later lookup for type depending extension using DocumentEntry.IsSpreadsheet and so on
                Document.DownloadType type = Document.DownloadType.xls;
                Document dt = new Document();
                dt.AtomEntry = m_DocInfo.m_DocEntry;
                DownloadInfo info = new DownloadInfo(dt, type);
                DownloadDocument(this, info);
                
                //get new DocumentEnty
                DoLookupRemoteCopy(m_DocInfo);

                Excel.Workbook wb = m_App.ActiveWorkbook;
                string tmpPath = wb.Path + "\\" + wb.Name + "_tmp.xls";

                FileInfo fileInfo = new FileInfo(tmpPath);
                FileStream stream = fileInfo.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                DoSaveDocumentContent(stream, info.m_Stream);

                MergeDocumentWith(tmpPath);
                
                //try to update document again
                DoUpdateDocument();
            }
            m_App.ActiveWorkbook.HighlightChangesOptions(Excel.XlHighlightChangesTime.xlAllChanges,
                                                         "Everyone",
                                                         missing); 
                          
            m_App.ActiveWorkbook.HighlightChangesOnScreen = true; 
            */
            throw new NotImplementedException();
        }

        private void BuildListOfUsers(IEnumerable users, Permission perm, AclFeed feed)
        {
            string permStr = null;
            switch (perm)
            {
                case Permission.reader: permStr = "reader"; break;
                case Permission.writer: permStr = "writer"; break;
            }

            foreach (object obj in users)
            {
                AclEntry entry = new AclEntry();
                entry.Scope = new AclScope();
                entry.Scope.Type = AclScope.SCOPE_USER;
                entry.Scope.Value = obj.ToString();
                entry.Role = new AclRole(permStr);

                feed.Entries.Add(entry);
            }
        }

        private void dummy()
        {}

        private void RefreshEtag()
        {
            DoLookupRemoteCopy(m_DocInfo);
        }

        public void UpdateDocumentInfo()
        {
            
            m_DocInfo = new DocumentInfo(m_App.ActiveDocument.FullName, m_App.ActiveDocument.Name);
            DoLookupRemoteCopy(m_DocInfo);
        }

        public void DoShareDocumentsAsync(IEnumerable users, Permission perm)
        {
            if (ShareDocument != null)
            {
                ShareInfoCollection info = new ShareInfoCollection(m_DocInfo.m_DocEntry);
                BuildListOfUsers(users, perm, info.m_Feed);

                DoOperationASync<ShareInfoCollection> operation = new DoOperationASync<ShareInfoCollection>(this.ShareDocument, info, this.RefreshEtag);
                operation.StartAction();
            }
        }

        public void DoOpenLocalCopyShared(string filePath)
        {
            object fileName = filePath;
            object readOnly = false;
            object isVisible = true;
            // Here is the way to handle parameters you don't care about in .NET
            object missing = System.Reflection.Missing.Value;

            
            try
            {
                Word.Document wb = m_App.Documents.Open(
                                            ref fileName, 
                                            ref missing, 
                                            ref readOnly, 
                                            ref missing, 
                                            ref missing, 
                                            ref missing, 
                                            ref missing, 
                                            ref missing, 
                                            ref missing, 
                                            ref missing, 
                                            ref missing, 
                                            ref isVisible,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing);

                wb.Save();

                m_App.ActiveDocument.TrackRevisions = true; 

                //ShareActiveDocument(m_App.ActiveDocument.FullName, (Word.WdSaveFormat)m_App.ActiveDocument.SaveFormat);
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message); 
            }
            
            //throw new NotImplementedException(); 
        }

        public void DoOpenLocalCopyNotShared(string filePath)
        {
            object fileName = filePath;
            object readOnly = false;
            object isVisible = true;
            // Here is the way to handle parameters you don't care about in .NET
            object missing = System.Reflection.Missing.Value;


            try
            {
                Word.Document wb = m_App.Documents.Open(
                                            ref fileName,
                                            ref missing,
                                            ref readOnly,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref isVisible,
                                            ref missing,
                                            ref missing,
                                            ref missing,
                                            ref missing);

                wb.Save();

                //m_App.ActiveDocument.TrackRevisions = true;
                //ShareActiveDocument(m_App.ActiveDocument.FullName, (Word.WdSaveFormat)m_App.ActiveDocument.SaveFormat);
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
            }

        }


        public void DoSaveDocumentContent(Stream target, Stream source)
        {
            try{
                if (target != null)
                {
                    int nBytes = 2048;
                    int count = 0;
                    Byte[] arr = new Byte[nBytes];

                    do
                    {
                        count = source.Read(arr, 0, nBytes);
                        target.Write(arr, 0, count);

                    } while (count > 0);
                    target.Flush();
                }
            }
            finally
            {
                if (target != null)
                    target.Close();
                if (source != null)
                    source.Close();
            }
        }

      

        public void DoCheckForActiveWorkbook(string name)
        {
        }

        public void DoSaveCredentials(Credentials cred)
        {
            try
            {
                using (DbManager.DBConnection con = m_db.CreateConnection())
                {
                    string command = "DELETE FROM Credentials";
                    con.ExecuteNonQuery(command, null);

                    if (cred.m_remember == true)
                    {
                        command = "INSERT INTO Credentials (Email, [Password]) VALUES (@Email, @Password)";

                        OleDbParameter[] param = new OleDbParameter[2];
                        param[0] = new OleDbParameter("@Email", cred.m_Cred.Username);
                        param[1] = new OleDbParameter("@Password", cred.m_password);

                        con.ExecuteNonQuery(command, param);
                    }
                }
            }
            catch (Exception e)
            {
                System.Console.Write(e.Message);
            }
        }

        public void DoLoadCredentials(Credentials cred)
        {
            try
            {
                using (DbManager.DBConnection con = m_db.CreateConnection())
                {
                    string command = "SELECT Email, [Password] FROM Credentials";

                    OleDbDataReader reader = con.ExecuteQuery(command, null);
                    reader.Read();
                    cred.m_Cred.Username = (string)reader["Email"];
                    cred.m_password = (string)reader["Password"];
                    cred.m_remember = true;
                }
            }
            catch (Exception e)
            {
                System.Console.Write(e.Message);
            }
        }

        public string GetInviteUri()
        { return m_DocInfo.m_DocEntry.AlternateUri.Content; }


        public void DoOpenDocumentInCloud(DocumentInfo info)
        {
            if (OpenDocumentInCloud != null)
                OpenDocumentInCloud(this, info);
        }

        public void DoGetDocumentAcl(ShareInfoCollection info)
        {
            if (GetDocumentAcl != null)
                GetDocumentAcl(this, info);
        }
     
    }

}
